<?php
/**
 * php GEM Project
 * 
 * LICENSE
 * 
 * PHP System Architecture Enabling Rapid Application Deployment and Scalability
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @copyright Copyright (C) 2010 Zeletron LLC (http://zeletron.com)
 * @license http://gem.zeletron.com/license
 */

/*
 * GEM/System.php
 * 
 * This class serves as a service control class. Here application services
 * can be registered, started, stopped, restarted, and accessed. This class 
 * also serves as a central registry for form objects. When this class
 * is saved to the session, forms and services registered to it 
 * maintain state. 
 * 
 * @author David Joly
 * @link http://zeletron.com
 * 
 * @category GEM
 */
class GEM_System
{
	/**
	 * The service access control list. This list can
	 * be used to provide access restriction at the service
	 * level.
	 * 
	 * @var GEM_Service_Acl
	 */
	protected $_serviceAcl;
	
	/**
	 * The service access control list. This list can
	 * be used to provide access restriction at the service
	 * level.
	 * 
	 * @var GEM_Model_Domain_Acl
	 */
	protected $_domainAcl;
	
	/**
	 * Registered Form Objects
	 * 
	 * @var array
	 */
	protected $_forms = array();
	
	/**
	 * Registered Services
	 * 
	 * array('serviceType' => array('service' => $serviceInstance, 'persist' => true/false))
	 * 
	 * @var array
	 */
	protected $_services = array();
	
	/**
	 * The Current User
	 * 
	 * @var GEM_Model_Domain_User
	 */
	protected $_user;
	
	/** 
	 * Singleton Instance
	 * 
	 * @var GEM_Service
	 */
	protected static $_instance;
	
	/** 
	 * Singleton Construct
	 * 
	 *  @param mixed array $options | optional
	 * */
	protected function __construct($options = null)
	{
		if(is_array($options)){
			if(array_key_exists('services',$options) && is_array($options['services'])){
				$this->registerServices($options['services']);
			}
		}
	}
	
	/**
	 * Returns the currently running service
	 * and starts the service if it has not yet
	 * been started.
	 * 
	 * @return GEM_System
	 */
	public static function instance()
	{
		return is_null(self::$_instance) ? self::start() : self::$_instance;
	}
	
	/**
	 * Starts the system and assigns to session namespace.
	 * 
	 * @param array $options optional
	 * @return GEM_System
	 * @uses Zend_Session_Namespace
	 */
	public static function start(array $options = array())
	{
		$zappNS = new Zend_Session_Namespace('GEM_System');
		if(empty($zappNS->system)){
			$zappNS->system = new self($options);
		}
		self::$_instance = $zappNS->system;
		return self::$_instance;
	}
	
	/**
	 * @return GEM_Service_Acl
	 */
	public function getServiceAcl()
	{
		if(is_null($this->_serviceAcl))
			$this->_serviceAcl = new GEM_Service_Acl();
		return $this->_serviceAcl;
	}
	
	/**
	 * Static wrapper fo getServiceAcl()
	 * 
	 * @return GEM_Service_Acl
	 */
	public static function serviceAcl()
	{
		return self::instance()->getServiceAcl();
	}
	
	/**
	 * @return GEM_Model_Domain_Acl
	 */
	public function getDomainAcl()
	{
		if(is_null($this->_domainAcl))
			$this->_domainAcl = new GEM_Model_Domain_Acl();
		return $this->_domainAcl;
	}
	
	/**
	 * Static wrapper fo getDomainAcl()
	 * 
	 * @return GEM_Model_Domain_Acl
	 */
	public static function domainAcl()
	{
		return self::instance()->getDomainAcl();
	}
	
	/**
	 * Returns an array of currently running services.
	 * 
	 * @return array
	 */
	public function running()
	{
		return array_keys($this->_services);
	}
	
	/**
	 * Returns the specified service object. Use the base class name (i.e., 
	 * the short name) like so:
	 * getService('User') rather than getService('GEM_Service_User')
	 * 
	 * @param string $service The service class type or short name
	 * @param boolean $autoPersist Persist the service? 
	 *  
	 * @return GEM_Service_Service
	 */
	public function getService($service, $autoPersist = true)
	{
		$class = $this->_serviceToClass($service);
		if(!array_key_exists($service, $this->_services)){
			$this->registerService($service,$autoPersist);
		}
		return $this->_services[$service]['service'];
	}
	
	/**
	 * Static wrapper for getService()
	 * 
	 * @param string $service The service class type
	 * @return GEM_Service_Service
	 */
	public static function service($service)
	{
		return self::instance()->getService($service);
	}
	
	/**
	 * Checks if the specified service has been registered.
	 * 
	 * @param string $service
	 * @return boolean
	 * @throws GEM_Exception
	 */
	public function isServiceRegistered($service)
	{
		$this->_serviceToClass($service);
		return array_key_exists($service,$this->_services) ? true : false;
	}
	
	/**
	 * Registers a service in the system if it has not 
	 * already been registered.
	 * 
	 * @param string $service
	 * @param boolean $autoPersist Persist the service? 
	 * @return GEM_System Enables a Fluent Interface
	 */
	public function registerService($service, $autoPersist = true)
	{
		$class = $this->_serviceToClass($service);
		if(!array_key_exists($service,$this->_services))
			$this->_services[$service] = array('service' => new $class(),'persist' => $autoPersist);
		return $this;
	}
	
	/**
	 * Registers an array of services with the system.
	 * 
	 * The $services parameter array can be populated by either
	 * a string representing the service type or an array. If
	 * a service is not to be persisted, an array must be used
	 * with a 'service' option set to the service type and a 'persist'
	 * option set to boolean false. Registered services will persist 
	 * by default.
	 * 
	 * $services => array(
	 * 		'serviceType',
	 * 		'serviceType2',
	 * 		array(
	 * 			'service' => 'serviceType3',
	 * 			'persist' => false
	 * 		)
	 * );
	 * 
	 * @param array $services
	 * @return GEM_System Enables a Fluent Interface
	 */
	public function registerServices(array $services = array())
	{
		foreach($services as $service){
			if(is_array($service)){
				$this->registerService($service['service'],$service['persist']);
			} else {
				$this->registerService($service);
			}
		}
		return $this;
	}
	
	/**
	 * 
	 * @param string $service
	 * @return string
	 * @throws GEM_Exception
	 */
	protected function _serviceToClass(&$service)
	{
		$service = ucfirst($service);
		$class = "GEM_Service_$service";
		if(!class_exists($class))
			throw new GEM_Exception("Service $class not Found!");
		return $class;
	}
	
	/**
	 * Unregisters a service, stopping it.
	 * 
	 * @param string $service The short name of the service
	 * @return GEM_System Enables a Fluent Interface
	 */
	public function unregisterService($service)
	{
		$this->_serviceToClass($service);
		if(array_key_exists($service, $this->_services)){
			unset($this->_services[$service]);
		}
		return $this;
	}
	
	/**
	 * @param string $form The form class short name
	 * @return Zend_Form
	 */
	public function getForm($form)
	{
		if(!array_key_exists($form, $this->_forms)){
			$this->registerForm($form);
		}
		return $this->_forms[$form];
	}
	
	/**
	 * Registers a form object
	 * 
	 * @param string $form The shortname of the form class
	 * @return GEM_System Enables a Fluent Interface
	 * @throws GEM_Exception
	 */
	public function registerForm($form)
	{
		$class = "GEM_Form_" . $form;
		if(!class_exists($class))
			throw new GEM_Exception("Form class $class not found!");
		$this->_forms[$form] = new $class();
		return $this;
	}
	
	/**
	 * Unregisters a form object
	 * 
	 * @param string $form The shortname of the form class
	 * @return GEM_System Enables a Fluent Interface
	 * @throws GEM_Exception
	 */
	public function unregisterForm($form)
	{
		$class = "GEM_Form_$form";
		if(!class_exists($class))
			throw new GEM_Exception("Form class $class not found!");
		unset($this->_forms[$form]);
		return $this;
	}
	
	/**
	 * Returns the Current User
	 * 
	 * @return GEM_Model_Domain_User
	 */
	public function getUser()
	{
		return $this->_user;
	}
	
	/**
	 * @param GEM_Model_Domain_User $user
	 * @return GEM_System Enables a Fluent Interface
	 * @throws GEM_Exception
	 */
	public function setUser(GEM_Model_Domain_User $user)
	{
		if(!$user instanceof GEM_Model_Domain_User)
			throw new GEM_Exception(__METHOD__ . " expects an object of GEM_Model_Domain_User");
		$this->_user = $user;
		return $this;
	}
	
	/**
	 * Unsets the current user
	 * 
	 * @return GEM_System Enables a Fluent Interface
	 */
	public function unsetUser()
	{
		unset($this->_user);
		return $this;
	}
	
	/**
	 * This is a static wrapper for unsetUser()
	 * 
	 * @return void
	 */
	public static function clearUser()
	{
		self::instance()->unsetUser();
	}
	
	/**
	 * Returns the Current User
	 * 
	 * @return GEM_Model_Domain_User
	 */
	public static function user()
	{
		$instance = self::instance();
		return $instance->getUser();
	}
	
	/**
	 * Checks if the system has a valid user.
	 * 
	 * @return boolean
	 */
	public static function hasUser()
	{
		return (self::user() instanceof GEM_Model_Domain_User) ? true : false;
	}
	
	/**
	 * @return void
	 */
	public function __sleep()
	{
		//Unsetting any services not to be persisted.
		foreach($this->_services as $key => $service){
			if($service['persist'] === false)
				unset($this->_services[$key]);
		}
		return array_keys(get_object_vars($this));
	}
	
}